package fr.nts.framework.front.form.html;

import java.lang.reflect.Field;
import java.util.List;
import java.util.Locale;
import java.util.Map;

import javax.annotation.Resource;

import org.apache.commons.beanutils.BeanUtils;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.springframework.context.support.AbstractMessageSource;
import org.springframework.stereotype.Component;

import com.google.common.collect.Lists;
import com.google.common.collect.Maps;

import fr.nts.framework.core.commun.enumeration.Encoding;
import fr.nts.framework.core.commun.tool.ApplicationContextHelper;
import fr.nts.framework.core.service.TemplateMaker;
import fr.nts.framework.front.form.AbstractForm;
import fr.nts.framework.front.form.annotation.Form;
import fr.nts.framework.front.form.annotation.FormField;
import fr.nts.framework.front.form.annotation.FormListOfValues;
import fr.nts.framework.front.form.manager.FormManager;
import fr.nts.framework.front.form.validation.FieldValidationResult;

/**
 * Générateur de code html lié au formulaire
 * 
 * @author sco
 * @version 1.0.0
 */
@Component
public class HtmlFormCreator{

	protected static final Logger log = org.slf4j.LoggerFactory.getLogger(HtmlFormCreator.class);
	
	private final static String FORM_TEMPLATE = "/template/nts-form.vm";

	private final static String DEFAULT_MSG_PREFIX = "simple.form.";
	private final static String DEFAULT_LABEL_PREFIX = "label.";
	private final static String DEFAULT_CHECKBOX_PREFIX = "checkbox.label.";
	private final static String FORM_SAVE_KEY = "button.save";
	
	private final static String FORM_TITLE_KEY = "form-title";
	private final static String FORM_CLASSNAME_KEY = "form-classname";
	private final static String FORM_SUBMIT_KEY = "form-submit-name";
	private final static String FORM_NAME_KEY = "form-name";
	private final static String FORM_ACTION_KEY = "form-action";
	private final static String FORM_METHOD_KEY = "form-method";
	private final static String FORM_INPUTS_KEY = "form-inputs";
	
	@Resource
	private FormManager formManager;
	
	@Resource(name = "ntsTemplateMaker")
	private TemplateMaker templateMaker;
	
	
	/**
	 * Génère le code HTML d'un formulaire via les api NTS
	 * 
	 * @param form Le formulaire que l'on souhaité généré en HTML
	 * @return Le code HTML du formulaire
	 */
	public String generateForm(AbstractForm form,
			                   final String formClass,
			                   final String title,
			                   final String method,
			                   final String action,
			                   final String bundle){
		
    	// Les infos du formulaire
    	Form formInfos = formManager.getFormInfos(form);
    	List<Field> fields = formManager.getNTSFieldList(form);

    	
		Map<String, Object> values = Maps.newHashMap();
		values.put(FORM_CLASSNAME_KEY, 
				   formClass);
		values.put(FORM_SUBMIT_KEY, 
				   getSubmitButtonName(formInfos, 
						               bundle));
		values.put(FORM_NAME_KEY, 
				   formInfos.name());
		values.put(FORM_ACTION_KEY, 
				   action);
		values.put(FORM_TITLE_KEY, 
				   title);
		values.put(FORM_METHOD_KEY, 
				   method);
		
		List<FormInput> inputs = Lists.newArrayList();
		for(Field field: fields){
			FormField formField = formManager.getFieldInfos(field);
			
			FormInput input = null;
			
			switch(formField.type()){
				case SIMPLE_TEXT:
				case SIMPLE_TEXT_PASSWORD:
					input = generateSimpleInput(form, 
                                                field);
					break;
				case LIST_OF_CHECKBOX:
					input = generateCheckBoxInput(form, 
                                                  field,
                                                  bundle);
					break;
				default:
					input = generateSimpleInput(form, 
                            field);
					break;
			}
			
			// Champ en erreur
			if(form.getValidationResult() != null && !form.getValidationResult().isValid()){
				FieldValidationResult result = form.getValidationResult().getErrorFields().get(field.getName());
				if(result != null){
					input.setHasError(true);
				}
			}
			
			input.setMandatory(formField.mandatory());
			input.setName(field.getName());
			input.setSize(String.valueOf(formField.size()));
			input.setType(formField.type());
			input.setLabel(getFieldLabelName(field.getName(),
					                         formInfos,
                                             bundle));
			
			inputs.add(input);
		}
		
		values.put(FORM_INPUTS_KEY, 
				   inputs);
		
		String formSTR = templateMaker.generateContent(values, 
				                                       FORM_TEMPLATE, 
				                                       Encoding.UTF8);
		
		return formSTR;
	}
	
    
	/**
	 * Génère un champ input texte simple
	 */
	private FormInput generateSimpleInput(AbstractForm form,
			                              Field field){
		FormInput input = new FormInput();

		input.setValues(getValue(form, 
				                 field));
		
		return input;
	}
	
	
	/**
	 * Génère une champ input checkbox
	 */
	private FormInput generateCheckBoxInput(AbstractForm form,
			                                Field field,
			                                String bundle){
		// Les infos du formulaire
    	Form formInfos = formManager.getFormInfos(form);
    	
		FormCheckbox input = new FormCheckbox();

		input.setValues(getValue(form, 
				                 field));
		FormListOfValues formListOfValues = field.getAnnotation(FormListOfValues.class);
		
		if(formListOfValues != null){
			String[] values = {StringUtils.EMPTY};
			// Utilisation d'une proporiété du bean formulaire pour remplir les valeurs de bases
			if(!StringUtils.isEmpty(formListOfValues.valuesGeneratorProperty())){
				try{
					values = BeanUtils.getArrayProperty(form, 
							                            field.getName());
				}catch(Exception e){
					log.warn("Erreur: ", e);
				}
			}else{
				values = formListOfValues.fixedValues();
			}
			
			input.setChoiceValues(values);
			
			String[] labels = new String[values.length];
			
			for(int i = 0; i < values.length; i++){
				labels[i] = getCheckboxLabelName(values[i],
						                         formInfos,
						                         bundle);
			}
			input.setChoiceLabels(labels);
		}
		
		return input;
	}
	
	
	/**
	 * Récupère la valeur d'un champ d'un formulaire
	 */
    private String[] getValue(AbstractForm form, 
    		                  Field field){
    	String[] value = formManager.getFieldValue(form, 
                                                   field);
    	
    	return value;
    }
    
    
	/**
	 * Retourne le label d'un champ check box
	 */
	private String getCheckboxLabelName(final String label,
			                            Form form,
			                            final String bundle){
		return getTxt(bundle,
				      DEFAULT_CHECKBOX_PREFIX + label,
				      form);
	};
	
	
	/**
	 * Retourne le label d'un champ
	 */
	private String getFieldLabelName(final String label,
			                         Form form,
			                         final String bundle){
		return getTxt(bundle,
				      DEFAULT_LABEL_PREFIX + label,
				      form);
	};
	
	
	/**
	 * Retourne le nom du bouton submit
	 */
	private String getSubmitButtonName(Form form,
			                           final String bundle){
		return getTxt(bundle,
				      FORM_SAVE_KEY,
				      form);
	};
	
	
    /**
     * Retourne le texte en gérant une eventuelle internalionalisation
     */
    private String getTxt(final String bundle,
    		              final String label,
    		              Form form){
    	if(getBundleMessage(bundle) != null){
    		String result = getBundleMessage(bundle).getMessage(DEFAULT_MSG_PREFIX + form.name() + "." + label, 
			    				                                null, 
			    				                                DEFAULT_MSG_PREFIX + form.name() + "." + label, 
			    				                                Locale.getDefault());
    		log.debug("Le clé [{}] a pour message: {}", DEFAULT_MSG_PREFIX + label, result);
    		
    		return result;
    	}
    	
    	return label;
    }
    
    
    /**
     * Le message bundle
     */
    private AbstractMessageSource getBundleMessage(final String bundle){
    	if(!StringUtils.isEmpty(bundle)){
    		try{
    			return (AbstractMessageSource)ApplicationContextHelper.getBean(bundle);
    		}catch(Exception e){
				log.warn("Erreur: {}", e);
			}
    	}
    	
    	return null;
    }
}